package com.cn.lotary.util;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.*;
import java.util.*;

/**
 *
 *
 * @author Jason 14-Jul-2011
 */
public class EntityProviderj {

    private static final String IMPORT_LIST = "import java.util.ArrayList;\r\nimport java.util.List;\r\n";

    public static String getType(String type) {
        String outcome = null;
        if ("INT".equals(type)) {
            outcome = "int";
        } else if ("VARCHAR".equals(type) || "ENUM".equals(type)) {
            outcome = "String";
        } else if ("DOUBLE".equals(type) || "BIT".equals(type)) {
            outcome = "Double";
        } else if ("TIMESTAMP".equals(type) || "DATETIME".equals(type)) {
            outcome = "Date";
        } else {
            System.err.println("other type: " + type);
        }
        return outcome;
    }
    public static final String RN = "\r\n";

    public static void main(String[] args) throws IOException {
        String driver = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://localhost:3306/crm";
        String user = "root";
        String password = "JasonChen";
        String packagePath = "com.cn.entity";
        String singleTable = null;
        List<MyEntity> entitys = getEntitys(driver, url, user, password, singleTable);
        for (MyEntity entity : entitys) {

            makeClass("/home/user192/lotary/projects/lotaryBase/test/com/cn/entity", entity.entityName, "package " + packagePath + ";\r\n" + entity.toString());
        }
    }

    private static void makeClass(String entityPath, String entityName, String entity) throws IOException {

        File file = new File(entityPath);
        if (!file.exists()) {
            file.mkdirs();
            System.out.println("create the path [" + entityPath + "]");
        }
        String newfullpath = entityPath + "/" + entityName + ".java";

        File newfile = new File(newfullpath);
        if (!newfile.exists()) {
            newfile.createNewFile();
        }

        FileWriter fileWriter = new FileWriter(newfile, true);

        fileWriter.write(entity);
        fileWriter.close();
    }

    private static List<MyEntity> getEntitys(String driver, String url, String user, String password, String singleTable) {
        Connection con = null;

        List<MyEntity> myEntitys = new ArrayList<MyEntity>();
        try {
            Class.forName(driver);
            con = DriverManager.getConnection(url, user, password);
            DatabaseMetaData databaseMetaData = con.getMetaData();
            ResultSet tables = databaseMetaData.getTables(null, "%", singleTable != null ? singleTable : "%", new String[]{"TABLE"});

            while (tables.next()) {
                String tableName = tables.getString("TABLE_NAME");
                String entityName = ObjectUtil.tableNameToEntityName(tableName);

                ResultSet importedKeys = databaseMetaData.getImportedKeys(null, null, tableName);
                Map<String, String> importedMap = new HashMap<String, String>();
                while (importedKeys.next()) {
                    importedKeys.getString("PKTABLE_CAT");
                    importedKeys.getString("PKTABLE_SCHEM");
                    importedKeys.getString("PKTABLE_NAME");
                    importedKeys.getString("PKCOLUMN_NAME");
                    importedKeys.getString("FKTABLE_CAT");
                    importedKeys.getString("FKTABLE_SCHEM");
                    importedKeys.getString("FKTABLE_NAME");
                    importedKeys.getString("FKCOLUMN_NAME");
                    importedKeys.getString("KEY_SEQ");
                    importedKeys.getString("UPDATE_RULE");
                    importedKeys.getString("DELETE_RULE");
                    importedKeys.getString("FK_NAME");
                    importedKeys.getString("PK_NAME");
                    importedKeys.getString("DEFERRABILITY");
                    importedMap.put(importedKeys.getString("FKCOLUMN_NAME"),
                            importedKeys.getString("PKTABLE_NAME"));
                }

                String tableComment = tables.getString("REMARKS");
                String TABLE_CAT = tables.getString("TABLE_CAT");
                String TABLE_SCHEM = tables.getString("TABLE_SCHEM");
                String TABLE_TYPE = tables.getString("TABLE_TYPE");
                String REMARKS = tables.getString("REMARKS");
                //  String TYPE_CAT = tables.getString("TYPE_CAT");
                // String TYPE_SCHEM = tables.getString("TYPE_SCHEM");
                //  String TYPE_NAME = tables.getString("TYPE_NAME");
                // String SELF_REFERENCING_COL_NAME = tables.getString("SELF_REFERENCING_COL_NAME");
                // String REF_GENERATION = tables.getString("REF_GENERATION");
                List<MyFilde> fildes = new ArrayList<MyFilde>();
                ResultSet exportedKeys = databaseMetaData.getExportedKeys(null, null, tableName);
                Map<String, String> exportedMap = new HashMap<String, String>();
                while (exportedKeys.next()) {
                    exportedMap.put(
                            exportedKeys.getString("FKCOLUMN_NAME"),
                            exportedKeys.getString("PKTABLE_NAME"));
                    String fkTableName = exportedKeys.getString("FKTABLE_NAME");
                    String fkEntityName = ObjectUtil.tableNameToEntityName(fkTableName);
                    String fkEntityFildeName = ObjectUtil.toLowerCase(fkEntityName) + "s";
                    String type = "List<" + fkEntityName + ">";
                    Comment comment = new Comment();
                    comment.setOneToMany(exportedKeys.getString("FKCOLUMN_NAME"));
                    fildes.add(new MyFilde(type, fkEntityFildeName, comment));
                }

                ResultSet columns = databaseMetaData.getColumns(null, "%", tableName, "%");

                while (columns.next()) {
                    String columnName = columns.getString("COLUMN_NAME");
                    String NULLABLE = columns.getString("NULLABLE");
                    String columnComment = columns.getString("REMARKS");
                    String columnType = columns.getString("DATA_TYPE");
                    String columnTYPE_NAME = columns.getString("TYPE_NAME");
                    String COLUMN_SIZE = columns.getString("COLUMN_SIZE");
                    String DECIMAL_DIGITS = columns.getString("DECIMAL_DIGITS");
                    String NUM_PREC_RADIX = columns.getString("NUM_PREC_RADIX");
                    String COLUMN_DEF = columns.getString("COLUMN_DEF");
                    String SQL_DATA_TYPE = columns.getString("SQL_DATA_TYPE");

                    String SQL_DATETIME_SUB = columns.getString("SQL_DATETIME_SUB");
                    String CHAR_OCTET_LENGTH = columns.getString("CHAR_OCTET_LENGTH");
                    String ORDINAL_POSITION = columns.getString("ORDINAL_POSITION");
                    String IS_NULLABLE = columns.getString("IS_NULLABLE");
                    //String SCOPE_CATLOG = columns.getString("SCOPE_CATLOG");
                    String SCOPE_SCHEMA = columns.getString("SCOPE_SCHEMA");
                    String SCOPE_TABLE = columns.getString("SCOPE_TABLE");
                    String SOURCE_DATA_TYPE = columns.getString("SOURCE_DATA_TYPE");
                    String IS_AUTOINCREMENT = columns.getString("IS_AUTOINCREMENT");

                    String fildeName = ObjectUtil.columnNameToFieldName(columnName);
                    String fildeType = getType(columnTYPE_NAME);

                    if (importedMap.keySet().contains(columnName)) {
                        fildeType = ObjectUtil.tableNameToEntityName(importedMap.get(columnName));
                    }

                    Comment comment = new Comment();
                    comment.setColumn(columnName);
                    comment.setNotNull(Boolean.valueOf(NULLABLE));
                    comment.setDate("Date".equals(fildeType));
                    if (COLUMN_SIZE != null && !"Date".equals(fildeType)) {
                        comment.setLength(Integer.valueOf(COLUMN_SIZE));
                    }

                    ResultSet primaryKeys = databaseMetaData.getPrimaryKeys(null, null, tableName);
                    while (primaryKeys.next()) {
                        primaryKeys.getString("TABLE_CAT");
                        primaryKeys.getString("TABLE_SCHEM");
                        primaryKeys.getString("TABLE_NAME");
                        primaryKeys.getString("KEY_SEQ");
                        primaryKeys.getString("PK_NAME");
                        String key = primaryKeys.getString("COLUMN_NAME");
                        comment.setId(key.equals(columnName));
                        System.err.println(key.equals(columnName));
                        System.err.println(key+"--"+columnName);
                    }

                    MyFilde filde = new MyFilde(fildeType, fildeName, comment);
                    fildes.add(filde);
                }



                MyEntity entity = new MyEntity(entityName, tableName, tableComment, fildes);
                myEntitys.add(entity);
                //break;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                con.close();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }
        //TODO:delete many to many
        for (MyEntity entity : myEntitys) {
            for (MyFilde filde : entity.fildes) {
                // filde.comment.id
            }
        }
        return myEntitys;
    }

    private static class MyEntity {

        private String entityName;
        private String tableName;
        private String tableComment;
        private List<MyFilde> fildes;

        public MyEntity(String entityName, String tableName, String tableComment, List<MyFilde> filde) {
            this.entityName = entityName;
            this.tableName = tableName;
            this.tableComment = tableComment;
            this.fildes = filde;
        }

        @Override
        public String toString() {
            StringBuilder outcome = new StringBuilder();
            boolean list = false;
            for (MyFilde filde : fildes) {
                list = filde.type.contains("List<");
                if (list) {
                    break;
                }
            }
            boolean date = false;
            for (MyFilde filde : fildes) {

                date = filde.type.contains("Date");
                if (date) {
                    break;
                }
            }

            if (list) {
                outcome.append("import java.util.ArrayList;").append(RN);
            }
            if (date) {
                outcome.append("import java.util.Date;").append(RN);
            }
            if (list) {
                outcome.append("import java.util.List;").append(RN);
            }
            outcome.append("import javax.persistence.*;").append(RN);
            if (list) {
                outcome.append("import javax.xml.bind.annotation.XmlRootElement;").append(RN);
                outcome.append("import javax.xml.bind.annotation.XmlTransient;").append(RN);
            }

            outcome.append("@Entity").append(RN);

            outcome.append("@Table(name = \"" + tableName + "\")").append(RN);
            if (list) {
                outcome.append("@XmlRootElement").append(RN);
            }

            outcome.append("public class ").append(entityName).append(" {").append(RN);
            for (MyFilde filde : fildes) {
                outcome.append(filde).append(RN);
            }
            for (MyFilde filde : fildes) {
                if (filde.type.contains("List<")) {
                    outcome.append("@XmlTransient").append(RN);
                }
                outcome.append("public ").append(filde.type).append(" get").append(ObjectUtil.toUpperCase(filde.filde)).append("(){").append(RN);
                if (filde.type.contains("List<")) {
                    outcome.append("if(").append(filde.filde).append(" == null) {").append(RN);
                    outcome.append(filde.filde).append(" = new ArrayList").append(filde.type.replace("List", "")).append("();").append(RN);
                    outcome.append("}").append(RN);
                }

                outcome.append("return ").append(filde.filde).append(";").append(RN).append("}").append(RN);

                outcome.append("public void set").append(ObjectUtil.toUpperCase(filde.filde)).append("(").append(filde.type).append(" ").append(filde.filde).append("){").append(RN);
                outcome.append("this.").append(filde.filde).append(" = ").append(filde.filde).append(";").append(RN).append("}").append(RN);
            }

            outcome.append("}");
            return outcome.toString();
        }
    }

    private static class MyFilde {

        private String type;
        private String filde;
        private Comment comment;

        @Override
        public String toString() {
            StringBuilder outcome = new StringBuilder();
            if (comment != null) {
                outcome.append(comment);
            }
            outcome.append("private ").append(type).append(" ").append(filde).append(";");
            return outcome.toString();
        }

        public MyFilde(String type, String filde, Comment comment) {
            this.type = type;
            this.filde = filde;
            this.comment = comment;
        }
    }

    private static class Comment {

        private boolean id;
        private String base;
        private String manyToMany;
        private String oneToMany;
        private String column;
        private boolean notNull;
        private boolean date;
        private String Size;
        private Integer length;

        public Comment() {
        }

        public void setSize(String Size) {
            this.Size = Size;
        }

        public void setBase(String base) {
            this.base = base;
        }

        public void setColumn(String column) {
            this.column = column;
        }

        public void setDate(boolean date) {
            this.date = date;
        }

        public void setId(boolean id) {
            this.id = id;
        }

        public void setLength(Integer length) {
            this.length = length;
        }

        public void setManyToMany(String manyToMany) {
            this.manyToMany = manyToMany;
        }

        public void setNotNull(boolean notNull) {
            this.notNull = notNull;
        }

        public void setOneToMany(String oneToMany) {
            this.oneToMany = oneToMany;
        }

        @Override
        public String toString() {
            StringBuilder outcome = new StringBuilder();
            if (id) {
                outcome.append("@Id").append(RN);
                outcome.append("@GeneratedValue(strategy = GenerationType.IDENTITY)").append(RN);
            }
            if (column != null) {
                outcome.append("@Column(name = \"" + column + "\"");
                if (notNull) {

                    outcome.append("nullable = ").append(notNull);
                }
                if (length != null && !id) {
                    outcome.append(",length = ").append(length);
                }
                outcome.append(")").append(RN);
            }
            if (notNull) {
                outcome.append("@Size(min = 1, max = " + length + ")").append(RN);
                outcome.append("@NotNull").append(RN);
            }
            if (date) {
                outcome.append("@Temporal(TemporalType.TIMESTAMP)").append(RN);
            }
            if (oneToMany != null) {
                outcome.append("@OneToMany(cascade = CascadeType.ALL, mappedBy = \"" + oneToMany + "\", fetch = FetchType.LAZY)").append(RN);
            }
            return outcome.toString();
        }
    }
}
